#include "ipc.h"
/*
* get_ipc_id() 从/proc/sysvipc/文件系统中获取IPC的id号
* pfile: 对应/proc/sysvipc/目录中的IPC文件分别为
* msg-消息队列,sem-信号量,shm-共享内存
* key: 对应要获取的IPC的id号的键值
*/
int get_ipc_id(char *proc_file,key_t key)
{
	FILE *pf;
	int i,j;
	char line[BUFSZ],colum[BUFSZ];
	if((pf = fopen(proc_file,"r")) == NULL){
		perror("Proc file not open");
		exit(EXIT_FAILURE);
	}
	fgets(line, BUFSZ,pf);
	while(!feof(pf)){
		i = j = 0;
		fgets(line, BUFSZ,pf);
		while(line[i] == ' ') i++;
		while(line[i] !=' ') colum[j++] = line[i++];
		colum[j] = '\0';
		if(atoi(colum) != key) continue;
		j=0;
		while(line[i] == ' ') i++;
		while(line[i] !=' ') colum[j++] = line[i++];
		colum[j] = '\0';
		i = atoi(colum);
		fclose(pf);
		return i;
	}
	fclose(pf);
	return -1;
}
/*
 * 信号灯上的PV操作
 * semid:信号灯数组标识符
 * semnum:信号灯数组下标
 * buf:操作信号灯的结构
 */
int P(int sem_id) // P操作
{
	struct sembuf buf;
	buf.sem_op = -1;
	buf.sem_num = 0;
	buf.sem_flg = SEM_UNDO;
	if((semop(sem_id,&buf,1)) <0) {
		perror("down error ");
		exit(EXIT_FAILURE);
	}
	return EXIT_SUCCESS;
}
int V(int sem_id) // V操作
{
	struct sembuf buf;
	buf.sem_op = 1;
	buf.sem_num = 0;
	buf.sem_flg = SEM_UNDO;
	if((semop(sem_id,&buf,1)) <0) {
		perror("up error ");
		exit(EXIT_FAILURE);
	}
	return EXIT_SUCCESS;
}
/*
 * set_sem函数建立一个具有n个信号灯的信号量
 * 如果建立成功,返回 一个信号灯数组的标识符sem_id
 * 输入参数:
 * sem_key 信号灯数组的键值
 * sem_val 信号灯数组中信号灯的个数
 * sem_flag 信号等数组的存取权限
 */
int set_sem(key_t sem_key,int sem_val,int sem_flg)
{
	int sem_id;
	Sem_uns sem_arg;
	//测试由sem_key标识的信号灯数组是否已经建立
	if((sem_id = get_ipc_id("/proc/sysvipc/sem",sem_key)) < 0 )
	{
		//semget新建一个信号灯,其标号返回到sem_id
		if((sem_id = semget(sem_key,1,sem_flg)) < 0)
		{
			perror("semaphore create error");
			exit(EXIT_FAILURE);
		}
		//设置信号灯的初值
		sem_arg.val = sem_val;
		if(semctl(sem_id,0,SETVAL,sem_arg) <0)
		{
			perror("semaphore set error");
			exit(EXIT_FAILURE);
		}
	}
	return sem_id;
}
/*
 * set_shm函数建立一个具有n个字节 的共享内存区
 * 如果建立成功,返回 一个指向该内存区首地址的指针shm_buf
 * 输入参数:
 * shm_key 共享内存的键值
 * shm_val 共享内存字节的长度
 * shm_flag 共享内存的存取权限
 */
char * set_shm(key_t shm_key,int shm_num,int shm_flg)
{
	int i,shm_id;
	char * shm_buf;
	//测试由shm_key标识的共享内存区是否已经建立
	if((shm_id = get_ipc_id("/proc/sysvipc/shm",shm_key)) < 0 )
	{
		//shmget新建 一个长度为shm_num字节的共享内存,其标号返回到shm_id
		if((shm_id = shmget(shm_key,shm_num,shm_flg)) <0)
		{
			perror("shareMemory set error");
			exit(EXIT_FAILURE);
		}
		//shmat将由shm_id标识的共享内存附加给指针shm_buf
		if((shm_buf = (char *)shmat(shm_id,0,0)) < (char *)0)
		{
			perror("get shareMemory error");
			exit(EXIT_FAILURE);
		}
		for(i=0; i<shm_num; i++) shm_buf[i] = 0; //初始为0
	}
	//shm_key标识的共享内存区已经建立,将由shm_id标识的共享内存附加给指针shm_buf
	else if((shm_buf = (char *)shmat(shm_id,0,0)) < (char *)0)
	{
		perror("get shareMemory error");
		exit(EXIT_FAILURE);
	}
	return shm_buf;
}
/*
 * set_msq函数建立一个//进程自定义的键盘中断信号处理函数
 * 如果建立成功,返回 一个消息队列的标识符msq_id
 * 输入参数:
 * msq_key 消息队列的键值
 * msq_flag 消息队列的存取权限
 */
int set_msq(key_t msq_key,int msq_flg)
{
	int msq_id;
	//测试由msq_key标识的消息队列是否已经建立
	if((msq_id = get_ipc_id("/proc/sysvipc/msg",msq_key)) < 0 )
	{
		//msgget新建一个消息队列,其标号返回到msq_id
		if((msq_id = msgget(msq_key,msq_flg)) < 0)
		{
			perror("messageQueue set error");
			exit(EXIT_FAILURE);
		}	
	}
	return msq_id;
}
//收到中断信号后释放申请的IPC
void sigcat()
{
	semctl(TP_Psyn_sem,0,IPC_RMID);
	semctl(PG_Psyn_sem,0,IPC_RMID);
	semctl(GT_Psyn_sem,0,IPC_RMID);
	semctl(TP_Pmtx_sem,0,IPC_RMID);
	semctl(PG_Pmtx_sem,0,IPC_RMID);
	semctl(GT_Pmtx_sem,0,IPC_RMID);
	semctl(TP_Csyn_sem,0,IPC_RMID);
	semctl(PG_Csyn_sem,0,IPC_RMID);
	semctl(GT_Csyn_sem,0,IPC_RMID);
	semctl(TP_Cmtx_sem,0,IPC_RMID);
	semctl(PG_Cmtx_sem,0,IPC_RMID);
	semctl(GT_Cmtx_sem,0,IPC_RMID);
	shmdt(tobacco);
	shmdt(paper);
	shmdt(glue);
	exit(0);
}
void ipc_init()
{
	key_t Tkey=101,Gkey=102,Pkey=103;//三个缓冲区
	buff_num=1;//缓冲区长度
	int shm_flg = IPC_CREAT | 0644;//共享内存读写权限
	tobacco=(char *)set_shm(Tkey,buff_num,shm_flg);//新建烟草放置区
	paper=(char *)set_shm(Pkey,buff_num,shm_flg);//新建纸张放置区
	glue=(char *)set_shm(Gkey,buff_num,shm_flg);//新建胶水放置区

	key_t TPpsk=201,PGpsk=202,GTpsk=203,TPpmk=204,PGpmk=205,GTpmk=206;
	key_t TPcsk=301,PGcsk=302,GTcsk=303,TPcmk=304,PGcmk=305,GTcmk=306;
	int sem_flg=IPC_CREAT|0644;
	//供应者同步信号灯初值设为缓冲区最大可用量
	int sem_val=buff_num;
	//获取供应者同步信号灯
	TP_Psyn_sem=set_sem(TPpsk,sem_val,sem_flg);
	PG_Psyn_sem=set_sem(PGpsk,sem_val,sem_flg);
	GT_Psyn_sem=set_sem(GTpsk,sem_val,sem_flg);
	//消费者同步信号灯初值为0，因为无产品可取
	sem_val=0;
	//获取消费者同步信号灯
	TP_Csyn_sem=set_sem(TPcsk,sem_val,sem_flg);
	PG_Csyn_sem=set_sem(PGcsk,sem_val,sem_flg);
	GT_Csyn_sem=set_sem(GTcsk,sem_val,sem_flg);	
	//生产者互斥信号灯初值为1
	sem_val=1;
	//获取供应者互斥信号灯
	TP_Pmtx_sem=set_sem(TPpmk,sem_val,sem_flg);
	PG_Pmtx_sem=set_sem(PGpmk,sem_val,sem_flg);
	GT_Pmtx_sem=set_sem(GTpmk,sem_val,sem_flg);	
	//消费者互斥信号灯初值为1
	sem_val=1;
	//获取消费者互斥信号灯
	TP_Cmtx_sem=set_sem(TPcmk,sem_val,sem_flg);
	PG_Cmtx_sem=set_sem(PGcmk,sem_val,sem_flg);
	GT_Cmtx_sem=set_sem(GTcmk,sem_val,sem_flg);	

}
